use crate::i18n_text::E_ARG_BAD;
use crate::id_factory::ID_FACTORY;

use crate::{
    datetime_format, i18n, search_type_id, search_type_name, Ares, SqlAttribute, EMPTY_FID,
};
use crate::{Astr, FieldId};
use chrono::{Local, NaiveDateTime};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::any::type_name;
use std::collections::HashMap;
use std::sync::Arc;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "T", content = "C")]
pub enum VddFieldType {
    #[serde(rename_all = "camelCase")]
    FtString {
        min_len: Option<usize>,
        max_len: Option<usize>,
        pattern: Option<String>,
    },
    #[serde(rename_all = "camelCase")]
    FtJson(usize),
    #[serde(rename_all = "camelCase")]
    FtYaml(usize),
    #[serde(rename_all = "camelCase")]
    FtInt {
        min_v: Option<i64>,
        max_v: Option<i64>,
        unit_label: Option<String>,
    },
    #[serde(rename_all = "camelCase")]
    FtFloat {
        min_v: Option<f64>,
        max_v: Option<f64>,
        unit_label: Option<String>,
        decimal_count: Option<u16>,
    },
    FtBool,
    FtAutoIncreaseInt {
        start: i64,
        step: i8,
        limit: i64,
    },
    FtAutoIncreaseIntGlobal,
    FtDate(Astr),
    FtTime(Astr),
    FtDateTime(Astr),
    FtFile(u16), // 最大上传大小，单位MB
    FtComputed(Arc<ComputeAction>),
    FtCode(String),
    #[serde(rename_all = "camelCase")]
    FtModel {
        provider: u64,
        display_field: FieldId,
    },
    FtModelSelf(FieldId),
    FtUserId,
    FtUserSelf,
}

pub const FT_STRING: u16 = 101;
pub const FT_JSON: u16 = 102;
pub const FT_YAML: u16 = 103;
pub const FT_INT: u16 = 201;
pub const C_FT_FLOAT: u16 = 202;
pub const C_FT_BOOL: u16 = 211;
pub const C_FT_AUTO_INCREASE: u16 = 212;
pub const C_FT_AUTO_INCREASE_GLOBAL: u16 = 291;
pub const C_FT_DATE: u16 = 301;
pub const C_FT_TIME: u16 = 302;
pub const C_FT_DATETIME: u16 = 303;
pub const C_FT_FILE: u16 = 401;
pub const C_FT_COMPUTED: u16 = 501;
pub const C_FT_CODE: u16 = 801;
pub const C_FT_MODEL: u16 = 901;
pub const C_FT_MODEL_SELF: u16 = 903;
pub const C_FT_USERID: u16 = 911;
pub const C_FT_USER_SELF: u16 = 912;

impl TryFrom<&Value> for VddFieldType {
    type Error = Astr;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        let ftc = value["fieldType"].as_u64().unwrap() as u16;
        let ft = match ftc {
            FT_STRING => {
                let min_len = value["minLen"].as_u64().map(|item| item as usize);
                let max_len = value["maxLen"].as_u64().map(|item| item as usize);
                let pattern = value["pattern"].as_str().map(|v| v.to_string());
                VddFieldType::FtString {
                    min_len,
                    max_len,
                    pattern,
                }
            }
            FT_JSON => {
                let max_len = value["maxLen"].as_u64().unwrap_or(0) as usize;
                VddFieldType::FtJson(max_len)
            }
            FT_YAML => {
                let max_len = value["maxLen"].as_u64().unwrap_or(0) as usize;
                VddFieldType::FtYaml(max_len)
            }
            FT_INT => {
                let min_v = value["minV"].as_i64();
                let max_v = value["maxV"].as_i64();
                let unit_label = value["unitLabel"].as_str().map(|v| v.to_string());
                VddFieldType::FtInt {
                    min_v,
                    max_v,
                    unit_label,
                }
            }
            C_FT_FLOAT => {
                let min_v = value["minV"].as_f64();
                let max_v = value["maxV"].as_f64();
                let unit_label = value["unitLabel"].as_str().map(|v| v.to_string());
                let decimal_count = value["decimalCount"].as_u64().map(|v| v as u16);
                VddFieldType::FtFloat {
                    min_v,
                    max_v,
                    unit_label,
                    decimal_count,
                }
            }
            C_FT_BOOL => VddFieldType::FtBool,
            C_FT_AUTO_INCREASE => VddFieldType::FtAutoIncreaseInt {
                start: value["start"].as_i64().unwrap_or(1),
                step: value["step"].as_i64().unwrap_or(1) as i8,
                limit: value["limit"].as_i64().unwrap_or(i64::MAX),
            },
            C_FT_AUTO_INCREASE_GLOBAL => VddFieldType::FtAutoIncreaseIntGlobal,
            C_FT_DATE => VddFieldType::FtDate(value["format"].as_str().unwrap_or("").into()),
            C_FT_TIME => VddFieldType::FtTime(value["format"].as_str().unwrap_or("").into()),
            C_FT_DATETIME => {
                VddFieldType::FtDateTime(value["format"].as_str().unwrap_or("").into())
            }
            C_FT_FILE => {
                VddFieldType::FtFile(value["maxMB"].as_u64().unwrap_or(u16::MAX as u64) as u16)
            }
            C_FT_COMPUTED => {
                VddFieldType::FtComputed(ComputeAction::try_from(value).unwrap().into())
            }
            C_FT_CODE => {
                VddFieldType::FtCode(value["codeSource"].as_str().unwrap_or("").to_string())
            }
            C_FT_MODEL => VddFieldType::FtModel {
                provider: value["lmId"].as_u64().unwrap_or(0),
                display_field: FieldId(value["lfId"].as_u64().unwrap_or(0)),
            },
            C_FT_MODEL_SELF => {
                VddFieldType::FtModelSelf(FieldId(value["lfId"].as_u64().unwrap_or(0)))
            }
            C_FT_USERID => VddFieldType::FtUserId,
            C_FT_USER_SELF => VddFieldType::FtUserSelf,
            _ => {
                panic!("unkonwn field type code {}", ftc);
            }
        };
        Ok(ft)
    }
}

impl Into<u16> for &VddFieldType {
    fn into(self) -> u16 {
        match self {
            VddFieldType::FtString { .. } => FT_STRING,
            VddFieldType::FtJson { .. } => FT_JSON,
            VddFieldType::FtYaml { .. } => FT_YAML,
            VddFieldType::FtInt { .. } => FT_INT,
            VddFieldType::FtFloat { .. } => C_FT_FLOAT,
            VddFieldType::FtBool => C_FT_BOOL,
            VddFieldType::FtAutoIncreaseInt { .. } => C_FT_AUTO_INCREASE,
            VddFieldType::FtAutoIncreaseIntGlobal => C_FT_AUTO_INCREASE_GLOBAL,
            VddFieldType::FtDate(_) => C_FT_DATE,
            VddFieldType::FtTime(_) => C_FT_TIME,
            VddFieldType::FtDateTime(_) => C_FT_DATETIME,
            VddFieldType::FtFile(_) => C_FT_FILE,
            VddFieldType::FtComputed(_) => C_FT_COMPUTED,
            VddFieldType::FtCode(_) => C_FT_CODE,
            VddFieldType::FtModel { .. } => C_FT_MODEL,
            VddFieldType::FtModelSelf(_) => C_FT_MODEL_SELF,
            VddFieldType::FtUserId => C_FT_USERID,
            VddFieldType::FtUserSelf => C_FT_USER_SELF,
        }
    }
}

impl VddFieldType {
    pub fn test_u16(code: u16, _source: Option<Astr>) -> Option<Self>
    where
        Self: Sized,
    {
        match code {
            FT_STRING => Some(VddFieldType::FtString {
                min_len: None,
                max_len: None,
                pattern: None,
            }),
            FT_JSON => Some(VddFieldType::FtJson(0)),
            FT_YAML => Some(VddFieldType::FtYaml(0)),
            FT_INT => Some(VddFieldType::FtInt {
                min_v: None,
                max_v: None,
                unit_label: None,
            }),
            C_FT_FLOAT => Some(VddFieldType::FtFloat {
                min_v: None,
                max_v: None,
                unit_label: None,
                decimal_count: None,
            }),
            C_FT_BOOL => Some(VddFieldType::FtBool),
            C_FT_AUTO_INCREASE => Some(VddFieldType::FtAutoIncreaseInt {
                start: 1,
                step: 1,
                limit: i64::MAX,
            }),
            C_FT_AUTO_INCREASE_GLOBAL => Some(VddFieldType::FtAutoIncreaseIntGlobal),
            C_FT_DATE => Some(VddFieldType::FtDate("".into())),
            C_FT_TIME => Some(VddFieldType::FtTime("".into())),
            C_FT_DATETIME => Some(VddFieldType::FtDateTime("".into())),
            C_FT_FILE => Some(VddFieldType::FtFile(u16::MAX)),
            C_FT_COMPUTED => Some(VddFieldType::FtComputed(Arc::new(
                ComputeAction::CaConsumeCount,
            ))),
            C_FT_CODE => Some(VddFieldType::FtCode("".to_string())),
            C_FT_MODEL => Some(VddFieldType::FtModel {
                provider: 0,
                display_field: EMPTY_FID,
            }),
            C_FT_MODEL_SELF => Some(VddFieldType::FtModelSelf(FieldId(0))),
            C_FT_USERID => Some(VddFieldType::FtUserId),
            C_FT_USER_SELF => Some(VddFieldType::FtUserSelf),
            _ => None,
        }
    }

    /// 判断一个字段类型是否接受多重字段设定
    pub fn accept_multi(&self) -> bool {
        match self {
            Self::FtJson { .. }
            | Self::FtBool
            | Self::FtAutoIncreaseInt { .. }
            | Self::FtAutoIncreaseIntGlobal
            | Self::FtComputed(..) => false,
            _ => true,
        }
    }

    /// 探测一个字段是否消费了其他模型
    pub fn get_provider(&self) -> Option<(u64, FieldId)> {
        match self {
            VddFieldType::FtModel {
                provider,
                display_field,
            } => Some((*provider, display_field.clone())),
            VddFieldType::FtModelSelf(dfid) => Some((0, dfid.clone())),
            _ => None,
        }
    }

    pub fn get_type_name(&self) -> Astr {
        match self {
            VddFieldType::FtString { .. } => search_type_name(type_name::<Self>(), "FtString"),
            VddFieldType::FtJson { .. } => search_type_name("VddFieldType", "FtJson"),
            VddFieldType::FtYaml { .. } => search_type_name("VddFieldType", "FtYaml"),
            VddFieldType::FtInt { .. } => search_type_name("VddFieldType", "FtInt"),
            VddFieldType::FtFloat { .. } => search_type_name("VddFieldType", "FtFloat"),
            VddFieldType::FtBool => search_type_name("VddFieldType", "FtBool"),
            VddFieldType::FtAutoIncreaseInt { .. } => {
                search_type_name("VddFieldType", "FtAutoIncreaseInt")
            }
            VddFieldType::FtAutoIncreaseIntGlobal => {
                search_type_name("VddFieldType", "FtAutoIncreaseIntGlobal")
            }
            VddFieldType::FtDate(_) => search_type_name("VddFieldType", "FtDate"),
            VddFieldType::FtTime(_) => search_type_name("VddFieldType", "FtTime"),
            VddFieldType::FtDateTime(_) => search_type_name("VddFieldType", "FtDateTime"),
            VddFieldType::FtFile(_) => search_type_name("VddFieldType", "FtFile"),
            VddFieldType::FtComputed(_) => search_type_name("VddFieldType", "FtComputed"),
            VddFieldType::FtCode(_) => search_type_name("VddFieldType", "FtCode"),
            VddFieldType::FtModel { .. } => search_type_name("VddFieldType", "FtModel"),
            VddFieldType::FtModelSelf(_) => search_type_name("VddFieldType", "FtModelSelf"),
            VddFieldType::FtUserId => search_type_name("VddFieldType", "FtUserId"),
            VddFieldType::FtUserSelf => search_type_name("VddFieldType", "FtUserSelf"),
        }
    }

    pub fn get_type_id(&self) -> u16 {
        match self {
            VddFieldType::FtString { .. } => search_type_id("VddFieldType", "FtString"),
            VddFieldType::FtJson { .. } => search_type_id("VddFieldType", "FtJson"),
            VddFieldType::FtYaml { .. } => search_type_id("VddFieldType", "FtYaml"),
            VddFieldType::FtInt { .. } => search_type_id("VddFieldType", "FtInt"),
            VddFieldType::FtFloat { .. } => search_type_id("VddFieldType", "FtFloat"),
            VddFieldType::FtBool => search_type_id("VddFieldType", "FtBool"),
            VddFieldType::FtAutoIncreaseInt { .. } => {
                search_type_id("VddFieldType", "FtAutoIncreaseInt")
            }
            VddFieldType::FtAutoIncreaseIntGlobal => {
                search_type_id("VddFieldType", "FtAutoIncreaseIntGlobal")
            }
            VddFieldType::FtDate(_) => search_type_id("VddFieldType", "FtDate"),
            VddFieldType::FtTime(_) => search_type_id("VddFieldType", "FtTime"),
            VddFieldType::FtDateTime(_) => search_type_id("VddFieldType", "FtDateTime"),
            VddFieldType::FtFile(_) => search_type_id("VddFieldType", "FtFile"),
            VddFieldType::FtComputed(_) => search_type_id("VddFieldType", "FtComputed"),
            VddFieldType::FtCode(_) => search_type_id("VddFieldType", "FtCode"),
            VddFieldType::FtModel { .. } => search_type_id("VddFieldType", "FtModel"),
            VddFieldType::FtModelSelf(_) => search_type_id("VddFieldType", "FtModelSelf"),
            VddFieldType::FtUserId => search_type_id("VddFieldType", "FtUserId"),
            VddFieldType::FtUserSelf => search_type_id("VddFieldType", "FtUserSelf"),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VddField {
    // 当从远程数据库表中导入表或视图时，字段的tag不允许修改，就是表中的字段名称
    pub id: FieldId,
    pub model_id: u64,
    pub display_order: i8,
    pub label: String,
    pub field_type: Arc<VddFieldType>,
    pub required: bool,
    pub multi: bool,
    pub unique: bool,
    #[serde(with = "datetime_format")]
    pub update_ts: NaiveDateTime,
    pub input_hint: Option<String>, //输入建议
}

impl VddField {
    pub fn new(model_id: u64, display_order: i8, label: &str, field_type: u16) -> Self {
        Self {
            id: FieldId(ID_FACTORY.lock().unwrap().lazy_generate()),
            model_id,
            display_order,
            label: label.to_string(),
            field_type: VddFieldType::test_u16(field_type, None).unwrap().into(),
            required: false,
            multi: false,
            unique: false,
            update_ts: Local::now().naive_local(),
            input_hint: None,
        }
    }

    /// 从json中更新定义
    pub fn update_form_json(&mut self, j: &Value) -> Ares {
        self.display_order = j["displayOrder"].as_i64().unwrap() as i8;
        self.label = j["label"].as_str().unwrap().to_string();
        self.field_type = VddFieldType::try_from(j).unwrap().into();
        self.required = j["required"].as_bool().unwrap_or(false);
        self.multi = j["multi"].as_bool().unwrap_or(false);
        self.unique = j["unique"].as_bool().unwrap_or(false);
        self.update_ts = Local::now().naive_local();
        self.input_hint = j["inputHint"].as_str().map(|v| v.to_string());
        if self.multi && !self.field_type.accept_multi() {
            Err("此类型字段不允许有多个值".into())
        } else {
            Ok(())
        }
    }

    /// transfer vector to hashmap
    pub fn v_to_m(v: &[VddFieldRef]) -> HashMap<FieldId, VddFieldRef> {
        let mut r = HashMap::<FieldId, VddFieldRef>::new();
        v.iter().for_each(|x| {
            r.insert(x.id.clone(), x.clone());
        });
        r
    }

    pub fn try_fit_str(&self, v: &str) -> Result<(), Astr> {
        fn fit_min_len(lbl: &str, len: usize, limit: usize) -> Result<(), Astr> {
            if len < limit {
                Err(i18n!(1231, lbl, &limit.to_string()))
            } else {
                Ok(())
            }
        }

        fn fit_max_len(lbl: &str, len: usize, limit: usize) -> Result<(), Astr> {
            if len > limit {
                Err(i18n!(1232, lbl, &limit.to_string()))
            } else {
                Ok(())
            }
        }

        match self.field_type.as_ref() {
            VddFieldType::FtString {
                max_len, min_len, ..
            } => {
                if let Some(min_len) = min_len {
                    fit_min_len(&self.label, v.len(), *min_len as usize)?
                }
                if let Some(max_len) = max_len {
                    fit_max_len(&self.label, v.len(), *max_len as usize)?
                }
                Ok(())
            }
            VddFieldType::FtJson(max_len) | VddFieldType::FtYaml(max_len) => {
                if *max_len > 0 {
                    fit_max_len(&self.label, v.len(), *max_len)?
                }
                Ok(())
            }
            _ => Err("Wrong FieldType".into()),
        }
    }

    pub fn fit_201(&self, v: i64) -> Result<(), Astr> {
        if let VddFieldType::FtInt { max_v, min_v, .. } = self.field_type.as_ref() {
            if let Some(min_v) = min_v {
                if v < *min_v {
                    return Err(i18n!(1233, &self.label));
                }
            }
            if let Some(max_v) = max_v {
                if v > *max_v {
                    return Err(i18n!(1234, &self.label));
                }
            }
            Ok(())
        } else {
            Err("field type not match".into())
        }
    }

    pub fn fit_202(&self, v: f64) -> Result<(), Astr> {
        if let VddFieldType::FtFloat { min_v, max_v, .. } = self.field_type.as_ref() {
            if min_v.is_some() {
                if v < min_v.unwrap() {
                    return Err(i18n!(1233, &self.label));
                }
            }
            if max_v.is_some() {
                if v > max_v.unwrap() {
                    return Err(i18n!(1234, &self.label));
                }
            }
            Ok(())
        } else {
            Err("field type not match".into())
        }
    }

    /// 判断一个字段是否链接到了某个字段，参数是链接到的字段
    pub fn used_field(&self, fid: &FieldId) -> bool {
        match self.field_type.as_ref() {
            VddFieldType::FtModel {
                provider: _,
                display_field: lf_id,
            } => lf_id == fid,
            _ => false,
        }
    }

    /// 判断一个字段是否是自动生成值的
    pub fn auto_generated(&self) -> bool {
        match self.field_type.as_ref() {
            VddFieldType::FtAutoIncreaseInt { .. }
            | VddFieldType::FtAutoIncreaseIntGlobal
            | VddFieldType::FtComputed { .. } => true,
            _ => false,
        }
    }

    /// 判断一个字段是否是文件字段
    pub fn is_file(&self) -> bool {
        match self.field_type.as_ref() {
            VddFieldType::FtFile(_) => true,
            _ => false,
        }
    }
}

impl SqlAttribute for VddField {
    fn quote_value(&self) -> bool {
        match self.field_type.as_ref() {
            VddFieldType::FtString { .. }
            | VddFieldType::FtDate(_)
            | VddFieldType::FtTime(_)
            | VddFieldType::FtDateTime(_)
            | VddFieldType::FtCode(_) => true,
            _ => false,
        }
    }
}

/// 计算型字段的组件
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "T", content = "C")]
pub enum ComputeAction {
    CaConsumeCount,                   // 此模型中此记录的全部消费计数
    CaConsumeCountByModel(u64),       // 此模型中此记录被某一模型（中所有记录）的消费计数
    CaValueMaxInRecord(Vec<FieldId>), // 此记录中几个字段值中的最大值
    CaValueMinInRecord(Vec<FieldId>),
    CaValueSumInRecord(Vec<FieldId>), // 求和
    CaValueAvgInRecord(Vec<FieldId>), // 求平均
}

impl TryFrom<&Value> for ComputeAction {
    type Error = Astr;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        let action = value["action"].as_str().unwrap();
        let params = value["args"]
            .as_array()
            .expect(i18n!(E_ARG_BAD).as_ref())
            .iter()
            .map(|arg| FieldId(arg.as_u64().unwrap()))
            .collect::<Vec<FieldId>>();
        match action {
            CA_REF_SUM => Ok(Self::CaConsumeCount),
            // 参数虽然全部转换为FieldId,此处却是虚假的,里面的是模型ID
            CA_REF_MOD => Ok(Self::CaConsumeCountByModel(
                params.first().clone().unwrap().0,
            )),
            CA_VAL_MAX => Ok(Self::CaValueMaxInRecord(params)),
            CA_VAL_MIN => Ok(Self::CaValueMinInRecord(params)),
            CA_VAL_SUM => Ok(Self::CaValueSumInRecord(params)),
            CA_VAL_AVG => Ok(Self::CaValueAvgInRecord(params)),
            _ => Err(i18n!(E_ARG_BAD)),
        }
    }
}

pub const CA_REF_SUM: &'static str = "CaConsumeCount";
pub const CA_REF_MOD: &'static str = "CaConsumeCountByModel";
pub const CA_VAL_MAX: &'static str = "CaValueMaxInRecord";
pub const CA_VAL_MIN: &'static str = "CaValueMinInRecord";
pub const CA_VAL_SUM: &'static str = "CaValueSumInRecord";
pub const CA_VAL_AVG: &'static str = "CaValueAvgInRecord";

pub type VddFieldRef = Arc<VddField>;
